package system.task.logger;

import lombok.Getter;
import lombok.SneakyThrows;
import org.springframework.util.StringUtils;
import system.task.TaskLogger;
import system.task.config.TaskConfig;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

public class TaskLoggerImpl implements TaskLogger, AutoCloseable {

    private final TaskConfig taskConfig;
    private DateTimeFormatter formatter;
    @Getter
    private final List<TaskLoggerWriter> logWriters;

    public TaskLoggerImpl(TaskLoggerContext taskLoggerContext) {
        this.taskConfig = taskLoggerContext.getConfig();
        this.runId = taskLoggerContext.getRunId();
        this.logWriters = taskLoggerContext.getLogWriters();
        this.path = StringUtils.hasText(taskConfig.getLogPath()) ? path(taskConfig.getLogPath()) : null;
        this.enable = this.path != null;
        //this.setPrefixTime("yyyy-MM-dd HH:mm:ss.SSS");
        this.setPrefixTime(this.taskConfig.getLogPrefixTime());
    }

    @Override
    public void setPrefixTime(String format) {
        if (StringUtils.hasText(format))
            this.formatter = DateTimeFormatter.ofPattern(format);
        else
            this.formatter = null;
    }

    private String path(String p) {
        if (!p.endsWith("/") && !p.endsWith("\\"))
            p = p + File.separator;
        p = p + runId.substring(0, 2) + File.separator + runId.substring(2, 4) + File.separator + runId + ".log";
        return p;
    }

    private final String runId;

    @Override
    public String getRunId() {
        return this.runId;
    }

    private final String path;

    @Override
    public String getPath() {
        return path;
    }

    private boolean _hasContent = false;

    @Override
    public boolean hasContent() {
        return _hasContent;
    }

    private final boolean enable;

    @Override
    public boolean isEnabled() {
        return enable;
    }

    private BufferedWriter writer = null;

    @SneakyThrows
    public void write(String s) {
        if (this.enable) {
            this.writeContent(s);
            //
            if (this.logWriters.size() > 0) {
                for (TaskLoggerWriter logWriter : this.logWriters) {
                    logWriter.info(null, s, false);
                }
            }
        }
    }

    @Override
    public void writeln(String var1) {
        if (this.enable) {
            DateTimeFormatter formatter = this.formatter;
            if (formatter == null) {
                this.writeContent(var1 + System.lineSeparator());
                //
                if (this.logWriters.size() > 0) {
                    for (TaskLoggerWriter logWriter : this.logWriters) {
                        logWriter.info(null, var1, true);
                    }
                }
            } else {
                String time = ZonedDateTime.now().format(formatter);
                this.writeContent(time + " " + var1 + System.lineSeparator());
                //
                if (this.logWriters.size() > 0) {
                    for (TaskLoggerWriter logWriter : this.logWriters) {
                        logWriter.info(time, var1, true);
                    }
                }
            }
        }
    }

    @Override
    public void error(String var1) {
        if (this.enable) {
            DateTimeFormatter formatter = this.formatter;
            if (formatter == null) {
                this.writeContent(var1 + System.lineSeparator());
                //
                if (this.logWriters.size() > 0) {
                    for (TaskLoggerWriter logWriter : this.logWriters) {
                        logWriter.info(null, var1, true);
                    }
                }
            } else {
                String time = ZonedDateTime.now().format(formatter);
                this.writeContent(time + " " + var1 + System.lineSeparator());
                //
                if (this.logWriters.size() > 0) {
                    for (TaskLoggerWriter logWriter : this.logWriters) {
                        logWriter.info(time, var1, true);
                    }
                }
            }
        }
    }

    @SneakyThrows
    protected synchronized void writeContent(String s) {
        if (this.enable) {
            if (writer == null) {
                File f = new File(this.path);
                File parent = f.getParentFile();
                if (!parent.exists()) {
                    parent.mkdirs();
                }
                writer = new BufferedWriter(new FileWriter(f), 1 * 1024 * 1024);
                _hasContent = true;
            }
            writer.write(s);
        }
    }

    @Override
    public synchronized void close() throws Exception {
        try {
            // close main writer
            BufferedWriter w = this.writer;
            if (w != null) {
                this.writer = null;
                w.flush();
                w.close();
            }
        } finally {
            // close writers
            List<TaskLoggerWriter> writers = this.logWriters;
            for (TaskLoggerWriter loggerWriter : writers) {
                try {
                    loggerWriter.close();
                } catch (Throwable throwable) {
                    // ignore close error
                }
            }
            writers.clear();
        }
    }

}
